Pular para o conteúdo principal

Guia de Uso dos Repositórios

Visão Geral

Este guia detalha como utilizar o padrão de Repositório para acesso a dados em nosso projeto. Este padrão abstrai a fonte de dados — seja o banco de dados local (RxDB) ou uma API remota — centralizando e organizando toda a lógica de acesso.

Adoção deste padrão nos ajuda a manter o código limpo, testável e fácil de manter. Além disso, otimiza a performance e a experiência do usuário através do cache local e das capacidades reativas fornecidas pelo RxDB.

Arquitetura

O fluxo de dados segue um caminho claro, projetado para garantir consistência e performance. Um componente React nunca acessa a fonte de dados diretamente. Em vez disso, ele utiliza hooks que orquestram o acesso através da camada de repositório.

Como Usar

1. Acessando um Repositório

Para acessar os repositórios em seus componentes React, utilize o hook useRepository.

import { useRepository } from "src/context/rxdb";

function MyComponent() {
// O nome da propriedade corresponde ao repositório desejado (ex: productsRepo)
const { productsRepo } = useRepository();

// Agora você pode usar `productsRepo` para acessar os dados.
// ...
}

O productsRepo pode ser null durante a renderização inicial, pois o banco de dados é inicializado de forma assíncrona. A seção seguinte explica como lidar com isso.

2. Buscando Dados com React Query

A maneira padrão para buscar dados de um repositório é com a biblioteca @tanstack/react-query. Ela simplifica o gerenciamento de estado assíncrono, cache e re-fetching.

O hook useQuery é a principal ferramenta. A opção enabled é crucial para garantir que a consulta só seja executada quando a instância do repositório estiver disponível.

Exemplo: Buscando todos os produtos

import { useRepository } from "src/context/rxdb";
import { useQuery } from "@tanstack/react-query";
import type { Product } from "src/types/product/product.types";

function ProductList() {
// 1. Obtenha a instância do repositório.
const { productsRepo } = useRepository();

// 2. Use `useQuery` para buscar os dados.
const { data, isLoading, error } = useQuery({
// `queryKey` é um array que identifica unicamente esta consulta.
queryKey: ["products", "all"],

// `queryFn` é a função que busca os dados.
queryFn: async () => {
// O `!` é seguro aqui por causa da verificação `enabled`.
return productsRepo!.fetchAll();
},

// `enabled` é a chave! A consulta fica em espera até que
// `productsRepo` não seja mais nulo.
enabled: !!productsRepo,
});

const products = data?.data || [];

if (isLoading) {
return <div>Carregando produtos...</div>;
}

if (error) {
return <div>Ocorreu um erro: {error.message}</div>;
}

return (
<ul>
{products.map((product) => (
<li key={product.CODPROD}>{product.DESCRPROD}</li>
))}
</ul>
);
}

Exemplos Avançados de Consulta

A queryKey do useQuery é fundamental. Ela deve incluir todos os parâmetros que podem invalidar a consulta, garantindo que o React Query busque novos dados quando os filtros mudarem.

Filtrando Produtos por Marca

import { useRepository } from "src/context/rxdb";
import { useQuery } from "@tanstack/react-query";
import { useState } from "react";

function FilteredProductList() {
const { productsRepo } = useRepository();
const [selectedBrand, setSelectedBrand] = useState("Apple");

const { data, isLoading } = useQuery({
// Adicione `selectedBrand` à queryKey.
// Se `selectedBrand` mudar, o React Query fará uma nova busca.
queryKey: ["products", "byBrand", selectedBrand],

queryFn: () => productsRepo!.fetchByBrand({ marca: selectedBrand }),

// A consulta depende do repositório estar pronto.
enabled: !!productsRepo,
});

// ...
}

O Poder do Mango Query Syntax

O RxDB utiliza uma sintaxe de consulta inspirada no MongoDB e popularizada pelo CouchDB, chamada Mango Query. Ela permite construir consultas complexas usando um objeto JSON.

Link Externo: Para uma referência completa dos operadores ($eq, $gt, $in, $regex, etc.), consulte a Documentação oficial do Mango Query.

Internamente, os métodos do nosso repositório (como fetchByBrand) constroem um selector do Mango Query.

Por exemplo, productsRepo.fetchByBrand({ marca: 'Apple' }) cria um seletor assim:

{
"selector": {
"MARCA": {
"$eq": "Apple"
}
}
}

Você também pode encontrar seletores mais complexos no código, como em fetchWithStock, que busca produtos com estoque disponível maior ou igual a um valor mínimo:

// Exemplo de dentro do repositório
const selector = {
ESTOQUEPADRAO: {
$elemMatch: {
// Encontra um elemento no array ESTOQUEPADRAO que corresponda...
DISPONIVEL: { $gte: 1 }, // ...a ter o campo DISPONIVEL maior ou igual a 1.
},
},
};

Repositórios Disponíveis

Repositório de Produtos (productsRepo)

  • fetchAll(params?: FetchPaginationParams): Busca todos os produtos com paginação.
  • fetchByBrand(params: FetchByBrandParams): Busca produtos por uma ou mais marcas.
  • fetchByManufacturer(params: FetchByManufacturerParams): Busca produtos por um ou mais fabricantes.
  • fetchByGroup(params: FetchByGroupParams): Busca produtos por um ou mais grupos.
  • search(params: SearchParams): Realiza uma busca textual nos produtos.
  • findByCodProd(params: FindByCodProdParams): Encontra um produto pelo seu código.
  • findByBarcode(params: FindByBarcodeParams): Encontra um produto pelo código de barras.
  • syncFromAPI(params: SyncFromAPIParams): Sincroniza um lote de produtos da API para o banco local.
  • clear(): Apaga todos os documentos da coleção.

Consulte a interface IProductRepository para a lista completa de métodos.

Limpeza de Dados no Logout

Importante: Não é necessário se preocupar em limpar os dados ao fazer logout. O sistema já está configurado para apagar todas as coleções do banco de dados local automaticamente quando o usuário sai da aplicação.

Criando Novos Repositórios

Para adicionar um repositório para uma nova coleção (ex: clients):

  1. Crie a interface do repositório: src/db/repositories/clients.repository.ts

    export interface IClientRepository {
    findById(id: number): Promise<Client | null>;
    // ...outros métodos
    }
  2. Crie a implementação RxDB: src/db/rxdb/repositories/clients.repository.ts

    import type { IClientRepository } from "../../repositories/clients.repository";

    export class ClientsRepositoryRxDB implements IClientRepository {
    constructor(private db: DBSchema) {}
    // ...implementação dos métodos usando this.db.clients
    }
  3. Atualize o provider.tsx (src/app/provider.tsx):

    • No dbStart, instancie ClientsRepositoryRxDB.
    • Registre o repositório usando register("clients", ...).
    • Configure o estado correspondente para disponibilizá-lo no RepositoryProvider.
    • Passe a nova instância para o RepositoryProvider.
    export default function Provider({
    children,
    }: {
    children: React.ReactNode;
    }) {
    const [productsRepo, setProductsRepo] =
    React.useState<IProductRepository | null>(null);

    const [clientsRepo, setClientsRepo] =
    React.useState<IClientRepository | null>(null);

    const dbStart = async () => {
    const db = await getDB();

    // Registro dos repositórios
    register("products", new ProductsRepositoryRxDB(db));
    register("clients", new ClientsRepositoryRxDB(db));

    // Estado interno
    setProductsRepo(new ProductsRepositoryRxDB(db));
    setClientsRepo(new ClientsRepositoryRxDB(db));
    };

    React.useEffect(() => {
    dbStart();
    }, []);

    return (
    <RepositoryProvider
    repositories={{
    productsRepo,
    clientsRepo,
    }}
    >
    {children}
    </RepositoryProvider>
    );
    }
  4. Atualize o Contexto (src/context/rxdb/index.tsx):

    • Adicione o novo repositório à interface RepositoryContextValue.
    • Atualize os tipos do RepositoryProvider para incluir o novo repositório (clientsRepo).